#include "ASprite.h"
#include "CGlobals.h"

using namespace irr;
using namespace video;
using namespace core;
using namespace io;

extern CGlobals* g;

EXT_DRAW_INFO::EXT_DRAW_INFO()
{
	REF_X = REF_Y = 0;
	REF_POINT = ASprite::LEFT|ASprite::TOP;
	BRIGHT_SCALE = 1;
	BRIGHT_HALO = false;
	GRAY_EFFECT = false;
	BLEND_COLOR = SColor(255, 255, 255, 255);
}

ASprite::ASprite()
{
	_nModules = 0;
	_modules_x = NULL;
	_modules_y = NULL;
	_modules_w = NULL;
	_modules_h = NULL;

	_frames_nfm = NULL;
	_frames_fm_start = NULL;
	_frames_rc = NULL;
	_frames_col = NULL;

	nFModules = 0;
	_nFModules = NULL;
	_anims_naf = NULL;
	_anims_af_start = NULL;
	_aframes = NULL;
	bs_flags = 0;
	ex_bs_flags = 0;
	_modules_image = NULL;
	
	_material = NULL;	
	indices[0] = 0;
	indices[1] = 2;
	indices[2] = 1;
	indices[3] = 0;
	indices[4] = 3;
	indices[5] = 2;

	vertices[0].Color = video::SColor(0xffffffff);
	vertices[1].Color = video::SColor(0xffffffff);
	vertices[2].Color = video::SColor(0xffffffff);
	vertices[3].Color = video::SColor(0xffffffff);

	_fmodules_correction = 0.0f;
}

ASprite::~ASprite()
{
	_nModules = 0;
	delete(_modules_x);
	_modules_x = NULL;
	delete(_modules_y);
	_modules_y = NULL;
	delete(_modules_w);
	_modules_w = NULL;
	delete(_modules_h);
	_modules_h = NULL;

	delete(_frames_nfm);
	_frames_nfm = NULL;
	delete(_frames_fm_start);
	_frames_fm_start = NULL;
	delete(_frames_rc);
	_frames_rc = NULL;
	delete(_frames_col);
	_frames_col = NULL;

	nFModules = 0;
	delete(_nFModules);
	_nFModules = NULL;
	delete(_anims_naf);
	_anims_naf = NULL;
	delete(_anims_af_start);
	_anims_af_start = NULL;
	delete(_aframes);
	_aframes = NULL;
	
	delete _material;
	if(_modules_image)
		g->driver->removeTexture(_modules_image);
	_modules_image = NULL;
}

void ASprite::LoadSprite(const char* spriteName)
{
	if(spriteName)
	{
		IReadFile* file = g->device->getFileSystem()->createAndOpenFile(spriteName);
		char* rawData;

		if(!file)
			return;

		rawData = new char[file->getSize()];
		s32 bytesRead = file->read(rawData, file->getSize());

		file->drop();

		Load(rawData);

		delete [] rawData;
	}
}

void ASprite::LoadTexture(const char* textureName)
{
	if(textureName)
	{
		ITexture* spriteTexture = g->driver->getTexture(textureName);
		BuildCacheImages(spriteTexture);
	}
}

void ASprite::BuildCacheImages(ITexture*	image)
{
	if(image)
	{
		_modules_image	= image;
		_material = new video::SMaterial();
		_material->MaterialType = EMT_TRANSPARENT_ALPHA_CHANNEL;
		_material->Lighting = false;
		_material->setTexture(0, _modules_image);
	}
	else
	{
		delete _material;
		_material = 0;
		if(_modules_image)
			g->driver->removeTexture(_modules_image);
		_modules_image = 0;	
	}
}

#define ReadByteFromRawData()	(rawdata[index] & 0xFF); index++
#define ReadShortFromRawData()	((rawdata[index] & 0xFF) | (rawdata[index+1] << 8)); index+=2
#define ReadIntFromRawData()	((rawdata[index] & 0xFF) | ((rawdata[index+1] & 0xFF) << 8) | ((rawdata[index+2] & 0xFF) << 16) | ((rawdata[index+3] & 0xFF) << 24)); index+=4
#define ReadFromRawData(dest, len)		memcpy(dest, (rawdata+index), len); index+=len

void ASprite::Load( char* rawdata)
{
	int index = 0;
// 	TRACE("AURORA: File size = " + file.length + "; Offset = " + offset);
	short bs_version = ReadShortFromRawData();

// 	TRACE("AURORA: BSprite version = 0x" + Integer.toHexString(bs_version));

	if (bs_version != BSPRITE_v003 && bs_version != BSPRITE_v004 && bs_version != BSPRITE_v005) 
	{
// 		if (AURORA_DEBUG) TRACE("AURORA: ERROR - Invalid BSprite version!");
		return;
	}

	bs_flags = ReadIntFromRawData();

	if (bs_version == BSPRITE_v004)
	{
		ex_bs_flags = ReadIntFromRawData();
	}
	int MaxRectUsed = 0;

	// Modules
	if ((bs_flags & BS_MODULES) != 0) 
	{
		_nModules = ReadShortFromRawData();

// 		if (AURORA_DEBUG) TRACE("AURORA: nModules = " + _nModules);

		if (_nModules > 0)
		{
			_modules_x = new short[_nModules];
			_modules_y = new short[_nModules];
			_modules_w = new short[_nModules];
			_modules_h = new short[_nModules];

			for (int i = 0; i < _nModules; i++)
			{
				char mtype = ReadByteFromRawData();
 				if (mtype == 0xFF) // -> MD_RECT
 				{ 
 					if ((bs_flags & BS_MODULES_WH_SHORT)!=0)
						index += 8;
					else
						index += 6;
 				}
				else if (mtype == 0xFE) // -> MD_FILL_RECT
 				{ 
 					if ((bs_flags & BS_MODULES_WH_SHORT)!=0)
						index += 8;
					else
						index += 6;
 				}
				else if (mtype == 0xFD) // -> MD_MARKER
 				{ 
 					if ((bs_flags & BS_MODULES_WH_SHORT)!=0)
						index += 4;
					else
						index += 2;
 				}
				else if (mtype == 0xFC) // -> MD_ARC
 				{ 
 					if ((bs_flags & BS_MODULES_WH_SHORT)!=0)
						index += 12;
					else
						index += 10;
 				}
 				else if (mtype == 0xFB) // -> MD_FILL_ARC
 				{ 
 					if ((bs_flags & BS_MODULES_WH_SHORT)!=0)
						index += 12;
					else
						index += 10;
 				}
				else if (mtype == 0xFA) // -> MD_TRIANGLE
 				{ 
					index += 12;
 				}
				else if (mtype == 0xF9) // -> MD_FILL_TRIANGLE
 				{ 
					index += 12;
 				}
 				else if (mtype == 0) // -> MD_IMAGE
				{
					if ((bs_flags & BS_MODULES_IMG)!=0)
					{
						index++;//TODO	
					}

					if ((bs_flags & BS_MODULES_XY_SHORT)!=0)
					{
						_modules_x[i] = ReadShortFromRawData();
						_modules_y[i] = ReadShortFromRawData();
					}
 					else	if ((bs_flags & BS_MODULES_XY)!=0)
 					{
 						_modules_x[i] = (short)ReadByteFromRawData();
 						_modules_y[i] = (short)ReadByteFromRawData();
 					}
					
					if ((bs_flags & BS_MODULES_WH_SHORT)!=0)
					{
						_modules_w[i] = ReadShortFromRawData();
						_modules_h[i] = ReadShortFromRawData();
					}
					else
					{
						_modules_w[i] = (short)ReadByteFromRawData();
						_modules_h[i] = (short)ReadByteFromRawData();
					}
				}
			}
		}

		//3d stuff
		//_vertices = new video::S3DVertex[_nModules][4];
	}

	//FModules
	if ((bs_flags & BS_FRAMES) != 0) 
	{
		nFModules = ReadShortFromRawData();

// 		if (AURORA_DEBUG) TRACE("AURORA: nFModules = " + nFModules);

		if (nFModules > 0) 
		{
			int _nFModuleslength;
			int fm_module_length = 0;
			if ((bs_flags & BS_FM_OFF_SHORT) != 0) 
			{
				fm_module_length = 6;
			} 
			else 
			{
				fm_module_length = 4;
			}

			if ((ex_bs_flags & BS_FM_FREE_ROTATE_SCALE) != 0)
			{
				fm_module_length += 6;
			}

			_nFModuleslength = nFModules*fm_module_length;
			_nFModules = new char[_nFModuleslength];

			ReadFromRawData(_nFModules,_nFModuleslength);

		}

		/*if (nFModules > 0) 
		{
			_fmodules_id = new byte[nFModules];
			_fmodules_ox = new short [nFModules];
			_fmodules_oy = new short [nFModules];
			_fmodules_flags = new byte[nFModules];

			for(int i = 0; i < nFModules; i++)
			{
				_fmodules_id[i] = ReadByteFromRawData();

				if ((bs_flags & BS_FM_OFF_SHORT) != 0)
				{
					_fmodules_ox[i] = ReadShortFromRawData();
					_fmodules_oy[i] = ReadShortFromRawData();
				}
				else
				{
					_fmodules_ox[i] = (short)ReadByteFromRawData();
					_fmodules_oy[i] = (short)ReadByteFromRawData();
				}

				if ((bs_flags & BS_FM_PALETTE) != 0)
					index++;

				_fmodules_flags[i] = ReadByteFromRawData();

				if ((ex_bs_flags & BS_FM_FREE_ROTATE_SCALE) != 0)
				{
					index += 6;
				}
			}
		}*/

		//Frames
/*
		if ((bs_flags & BS_FRAME_RECTS) != 0)
		{

			int rectCount = (short)((file[offset++]&0xFF) + ((file[offset++]&0xFF)<<8));
			if (AURORA_DEBUG) TRACE("AURORA: rectCount = " + rectCount);
			// need to remove
			/ *			int _nFrames = rectCount>>1;//we use 2 rects
			_frames_rc = new byte[_nFrames<<2];
			_frames_coll = new byte[_nFrames<<2];
			_frames_attack = new byte[_nFrames<<2];

			for (int i = 0; i < _nFrames; i++)
			{
			_frame_rects[i*4 + 0] = _frames_coll[i*4 + 0] = file[offset++];
			_frames_rc[i*4 + 1] = _frames_coll[i*4 + 1] = file[offset++];
			_frames_rc[i*4 + 2] = _frames_coll[i*4 + 2] = file[offset++];
			_frames_rc[i*4 + 3] = _frames_coll[i*4 + 3] = file[offset++];
			_frames_attack[i*4 + 0] = file[offset++];
			_frames_attack[i*4 + 1] = file[offset++];
			_frames_attack[i*4 + 2] = file[offset++];
			_frames_attack[i*4 + 3] = file[offset++];
			}
			* /
			_frame_rects = new byte[rectCount << 2];
			System.arraycopy(file, offset, _frame_rects , 0 , _frame_rects.length);
			offset += _frame_rects.length;

		}
*/	

		int nFrames = ReadShortFromRawData();
		_nFrames = nFrames;

// 		if (AURORA_DEBUG) TRACE("AURORA: nFrames = " + nFrames);

		if (nFrames > 0) 
		{
			short frame_rects_offset = 0;
			{		
				_frames_nfm = new short[nFrames];
				_frames_fm_start = new short[nFrames];
				for (int i = 0; i < nFrames; i++) 
				{
					if ((bs_flags & BS_NFM_1_BYTE) != 0)
					{
						_frames_nfm[i] = ReadByteFromRawData();
					}
					else
					{
						_frames_nfm[i] = ReadShortFromRawData();
					}

					_frames_fm_start[i] = ReadShortFromRawData();

					if ((bs_flags & BS_FRAME_RECTS) != 0) 
						index++;
				}
			}                                 
                                              
			if ((bs_flags & BS_SKIP_FRAME_RC) == 0) 
			{
				if ((bs_flags & BS_FM_OFF_SHORT) != 0) 
				{
					_frames_rc = new char[nFrames<<3];
					ReadFromRawData(_frames_rc,nFrames<<3);
				}
				else
				{
					_frames_rc = new char[nFrames<<2];
					ReadFromRawData(_frames_rc,nFrames<<2);
				}
			}                                 
                                              
			if ((bs_flags & BS_FRAME_COLL_RC) != 0) 
			{
				if ((bs_flags & BS_FM_OFF_SHORT) != 0)
				{
					_frames_col = new char[nFrames<<3];
					ReadFromRawData(_frames_col,nFrames<<3);
				}
				else
				{
					_frames_col = new char[nFrames<<2];
					ReadFromRawData(_frames_col,nFrames<<2);
				}
			}                                 
		}                                    
	}


	//AFrames
	if ((bs_flags & BS_ANIMS) != 0) 
	{
		_nAFrames = ReadShortFromRawData();

// 		if (AURORA_DEBUG) TRACE("AURORA: nAFrames = " + _nAFrames);

		if (_nAFrames > 0) 
		{
			int _nAFramesLength;
			if ((bs_flags & BS_AF_OFF_SHORT) != 0)
				_nAFramesLength = 7;
			else
				_nAFramesLength = 5;

			_aframes = new char[_nAFrames*_nAFramesLength];

			ReadFromRawData(_aframes,_nAFrames*_nAFramesLength);
		}

		//Animsations

		int nAnims = ReadShortFromRawData();

		_nAnims = nAnims;
// 		if (AURORA_DEBUG) TRACE("AURORA: nAnims = " + nAnims);

		if (nAnims > 0) 
		{
			_anims_naf = new short[nAnims];
			_anims_af_start = new short[nAnims];
			for (int i = 0; i < nAnims; i++) 
			{
				if ((bs_flags & BS_NAF_1_BYTE) != 0)
				{
					_anims_naf[i] = ReadByteFromRawData();
				}
				else
				{
					_anims_naf[i] = ReadShortFromRawData();
				}

				_anims_af_start[i] = ReadShortFromRawData();
			}
		}
	}

	if (_nModules <= 0) 
	{
// 		if (AURORA_DEBUG) TRACE("AURORA: WARNING - This sprite has 0 nModules");
		return;
	}

}

void ASprite::PaintAFrame(int anim, int aframe, int posX, int posY, int flags, int hx, int hy ,int alpha,EXT_DRAW_INFO* info)
{
	int off = (_anims_af_start[anim] + aframe) * 5;
	int frame = _aframes[off] & 0xFF;

	if (USE_INDEX_EX_AFRAMES) {
		frame |= ((_aframes[off + 4] & FLAG_INDEX_EX_MASK) << INDEX_EX_SHIFT);
	}
	int temp_hx = _aframes[off + 2];
	int temp_hy = _aframes[off + 3];

	if (info == NULL)
	{
		if ((flags & FLAG_FLIP_X) != 0) hx += temp_hx;
		else hx -= temp_hx;
		if ((flags & FLAG_FLIP_Y) != 0) hy += temp_hy;
		else hy -= temp_hy;
	}
	else
	{
		info->REF_X += temp_hx;
		info->REF_Y += temp_hy;
	}
	PaintFrame( frame, posX - hx, posY - hy, flags ^ (_aframes[off + 4] & 0x0F), hx, hy,alpha,0,100,100,info);

}

void ASprite::PaintFModule( int frame, int fmodule, int posX, int posY, int flags, int hx, int hy,int alpha , int rotate,int xscale,int yscale ,EXT_DRAW_INFO* info )
{
	int off;
	int fm_flags;

	int ex_off = 0;
	if ((bs_flags & BS_FM_OFF_SHORT) != 0) 
	{
		if ((ex_bs_flags & BS_FM_FREE_ROTATE_SCALE) != 0)
			off = (_frames_fm_start[frame] + fmodule) * 12;
		else
			off = (_frames_fm_start[frame] + fmodule) * 6;
		fm_flags = _nFModules[off + 5] & 0xFF;
	} 
	else 
	{
		off = (_frames_fm_start[frame] + fmodule) << 2;
		fm_flags = _nFModules[off + 3] & 0xFF;
	}

	int index = _nFModules[off] & 0xFF;
	if (USE_INDEX_EX_FMODULES) 
	{
		index |= ((fm_flags & FLAG_INDEX_EX_MASK) << INDEX_EX_SHIFT);
	}

	EXT_DRAW_INFO*	tempinfo = NULL;
	int temp_hx = (short)((_nFModules[off + 1] & 0xFF) + ((_nFModules[off + 2] & 0xFF) << 8));
	int temp_hy = (short)((_nFModules[off + 3] & 0xFF) + ((_nFModules[off + 4] & 0xFF) << 8));
	if (info == NULL)
	{
		if ((flags & FLAG_FLIP_X) != 0) posX -= temp_hx;
		else posX += temp_hx;
		if ((flags & FLAG_FLIP_Y) != 0) posY -= temp_hy;
		else posY += temp_hy;
		
		if (USE_HYPER_FM && ((fm_flags & FLAG_HYPER_FM) != 0)) 
		{
			PaintFrame(index, posX, posY, flags ^ (fm_flags & 0x0F), hx, hy,alpha,rotate,xscale,yscale,tempinfo);
		} 
		else 
		{
			if ((flags & FLAG_FLIP_X) != 0) posX -= _modules_w[index]/* & 0xFF*/;
			if ((flags & FLAG_FLIP_Y) != 0) posY -= _modules_h[index]/* & 0xFF*/;

			if ((ex_bs_flags & BS_FM_FREE_ROTATE_SCALE) == 0)
				PaintModule(index, posX, posY, flags ^ (fm_flags & 0x0F),rotate,xscale,yscale,alpha,tempinfo);
			else
			{
				int	s_rotate = (short)((_nFModules[off + 6] & 0xFF) + ((_nFModules[off + 7] & 0xFF) << 8));
				int	s_xscale = (short)((_nFModules[off + 8] & 0xFF) + ((_nFModules[off + 9] & 0xFF) << 8));
				int	s_yscale = (short)((_nFModules[off + 10] & 0xFF) + ((_nFModules[off + 11] & 0xFF) << 8));
				PaintModule(index, posX, posY, flags ^ (fm_flags & 0x0F),s_rotate + rotate,s_xscale * xscale / 100,s_yscale * yscale / 100,alpha,tempinfo);
			}
		}
	}
	else
	{
 		EXT_DRAW_INFO	infocopy(*info);
		infocopy.REF_X += temp_hx;
		infocopy.REF_Y += temp_hy;
 		tempinfo = &infocopy;

		if (USE_HYPER_FM && ((fm_flags & FLAG_HYPER_FM) != 0)) 
		{
			PaintFrame(index, posX, posY, flags ^ (fm_flags & 0x0F), hx, hy,alpha,rotate,xscale,yscale,tempinfo);
		} 
		else 
		{
			if ((flags & FLAG_FLIP_X) != 0) posX -= _modules_w[index]/* & 0xFF*/;
			if ((flags & FLAG_FLIP_Y) != 0) posY -= _modules_h[index]/* & 0xFF*/;

			if ((ex_bs_flags & BS_FM_FREE_ROTATE_SCALE) == 0)
				PaintModule(index, posX, posY, flags ^ (fm_flags & 0x0F),rotate,xscale,yscale,alpha,tempinfo);
			else
			{
				int	s_rotate = (short)((_nFModules[off + 6] & 0xFF) + ((_nFModules[off + 7] & 0xFF) << 8));
				int	s_xscale = (short)((_nFModules[off + 8] & 0xFF) + ((_nFModules[off + 9] & 0xFF) << 8));
				int	s_yscale = (short)((_nFModules[off + 10] & 0xFF) + ((_nFModules[off + 11] & 0xFF) << 8));
				PaintModule(index, posX, posY, flags ^ (fm_flags & 0x0F),s_rotate + rotate,s_xscale * xscale / 100,s_yscale * yscale / 100,alpha,tempinfo);
			}
		}

	}

}

void ASprite::PaintModule(int module, int posX, int posY, int flags, int rotate, int xscale, int yscale,int alpha, EXT_DRAW_INFO* info)
{
	//A_ASSERT(module < _nModules);
	int srcPosX = _modules_x[module];
	int srcPosY = _modules_y[module];
	int sizeX = _modules_w[module];// & 0xFF;
	int sizeY = _modules_h[module];// & 0xFF;

	if (sizeX <= 0 || sizeY <= 0)
		return;

	//*TODO : need to implement	
	//./CGfx *g = CGfx::GetInstance();
	if (_modules_image != NULL)
	{
/*
		if (Graphics::GetInstance()->IsWideScreen())
		{
			if ((flags & 4) == 0)
			{
				int scal_x = ((int(posX / 1.334375)) * 1.334375);
				int delta_x = scal_x - posX;
				int pos2 = posX + sizeX;
				int delta_w = ABS(((int(pos2 / 1.334375)) * 1.334375) - scal_x - sizeX);
				g->DrawBmpRegion(posX + delta_x, posY, sizeX, sizeY, _modules_image, _modules_x[module],_modules_y[module], flags,255,rotate,(xscale +(float(delta_w)) / sizeX) / 100.0,yscale / 100.0);
			}
			else
			{
				int scal_x = ((int(posX / 1.334375)) * 1.334375);
				int delta_x = scal_x - posX;
				int pos2 = posX + sizeY;
				int delta_h = ABS(((int(pos2 / 1.334375)) * 1.334375) - scal_x - sizeY);
				g->DrawBmpRegion(posX+delta_x, posY, sizeX, sizeY, _modules_image, _modules_x[module],_modules_y[module], flags,255,rotate,xscale/ 100.0,(yscale +(float(delta_h)) / sizeY)  / 100.0);
			}
		}
		else
*/
		//./g->DrawBmpRegion(posX, posY, sizeX, sizeY, _modules_image, _modules_x[module],_modules_y[module], flags,alpha,rotate,xscale / 100.0,yscale / 100.0,info);

		rect<s32> destRect;
		rect<s32> srcRect;
		rect<s32> clipRect;
		SColor color;

		position2d<s32> pos(posX, posY);
		srcRect = rect<s32>(srcPosX, srcPosY, srcPosX + sizeX, srcPosY + sizeY);
		destRect = rect<s32>(posX, posY, posX + sizeX, posY + sizeY);

		if (info != NULL)
			color = info->BLEND_COLOR;
		else
			color = SColor(255, 255, 255, 255);

		//if (0 == flags)
		//{
		g->driver->draw2DImage(_modules_image, pos, srcRect, &destRect, color, true);
		//}
		//else
		//{
		//	switch(flags)
		//	{
		//	case FLAG_FLIP_X:
		//		{
		//			SColor col[4];
		//			col[0] = SColor(255, 255, 255, 255);
		//			col[1] = SColor(255, 255, 255, 255);
		//			col[2] = SColor(255, 255, 255, 255);
		//			col[3] = SColor(255, 255, 255, 255);
		//			//rect<s32> destRect = rect<s32>(posX + sizeX, posY + sizeY, posX, posY);

		//			getGame()->m_pVideoDriver->draw2DImage(_modules_image, destRect, srcRect, &destRect, col, true);
		//		}
		//		break;

		//	case FLAG_FLIP_Y:
		//		{}
		//		break;

		//	case FLAG_ROT_90:
		//		{}
		//		break;

		//	case FLAG_FREE_ROT:
		//		{}
		//		break;
		//	}
		//}
	}

	int m = 0;
/*
	Image img = null;
	if ((_modules_image != null) &&
		//(_crt_pal < _modules_image.length) &&
		(_modules_image[_crt_pal] != null))
		img = _modules_image[_crt_pal][0];

	if (img == null && _crt_pal != 0)
		img = _modules_image[_crt_pal - 1][0];//TODO :_crt_pal - 1 problem
	// 			sizeX = img.getWidth();
	// 			sizeY = img.getHeight();
	// 			g.setClip(new_cx, new_cy, new_endcx - new_cx, new_endcy - new_cy);
	int spriteflag = 0;

	if ((flags & FLAG_FLIP_X )!=0)
		spriteflag ^= Sprite.TRANS_MIRROR;
	if ((flags & FLAG_FLIP_Y )!=0)
		spriteflag ^= Sprite.TRANS_MIRROR_ROT180;
	if ((flags & FLAG_ROT_90 )!=0)
		spriteflag ^= Sprite.TRANS_ROT90;

#ifdef 	USE_SHORT_MODULEXY	
	if ((flags & BS_MODULES_XY_SHORT)!=0)
		g.drawRegion(img, _modules_x[module]&0xFFFF, _modules_y[module]&0xFFFF, (_modules_w[module]&0xFF), (_modules_h[module]&0xFF), spriteflag,posX, posY, cGame.TOP|cGame.LEFT);
	else 
		g.drawRegion(img, _modules_x[module]&0xFFFF, _modules_y[module]&0xFFFF, (_modules_w[module]&0xFF), (_modules_h[module]&0xFF), spriteflag,posX, posY, cGame.TOP|cGame.LEFT);
#else
*/

}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 3D drawing
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

void ASprite::PaintAFrame3D(int anim, int aframe, const core::vector3df& pos, int flags, f32 hx, f32 hy, f32 xscale, f32 yscale)
{
	int off = (_anims_af_start[anim] + aframe) * 5;
	int frame = _aframes[off] & 0xFF;

	if (USE_INDEX_EX_AFRAMES) {
		frame |= ((_aframes[off + 4] & FLAG_INDEX_EX_MASK) << INDEX_EX_SHIFT);
	}
	int temp_hx = _aframes[off + 2];
	int temp_hy = _aframes[off + 3];

	if ((flags & FLAG_FLIP_X) != 0) hx += temp_hx;
	else hx -= temp_hx;
	if ((flags & FLAG_FLIP_Y) != 0) hy += temp_hy;
	else hy -= temp_hy;
	
	PaintFrame3D( frame, pos, flags ^ (_aframes[off + 4] & 0x0F), hx, hy, xscale, yscale);
}

void ASprite::PaintFrame3D(int frame, const core::vector3df& pos, int flags, f32 hx, f32 hy, f32 xscale, f32 yscale)
{
	int nFModules = _frames_nfm[frame]&0xFF;

	for (int fmodule = 0; fmodule < nFModules; fmodule++)
	{
		PaintFModule3D(frame, fmodule, pos, flags, hx, hy, xscale, yscale);
		_fmodules_correction += 0.005f;
	}
	_fmodules_correction = 0.0f;
}

void ASprite::PaintFModule3D(int frame, int fmodule, const core::vector3df& pos, int flags, f32 hx, f32 hy, f32 xscale, f32 yscale)
{
	int off;
	int fm_flags;

	int ex_off = 0;
	if ((bs_flags & BS_FM_OFF_SHORT) != 0) 
	{
		if ((ex_bs_flags & BS_FM_FREE_ROTATE_SCALE) != 0)
			off = (_frames_fm_start[frame] + fmodule) * 12;
		else
			off = (_frames_fm_start[frame] + fmodule) * 6;
		fm_flags = _nFModules[off + 5] & 0xFF;
	} 
	else 
	{
		off = (_frames_fm_start[frame] + fmodule) << 2;
		fm_flags = _nFModules[off + 3] & 0xFF;
	}

	int index = _nFModules[off] & 0xFF;
	if (USE_INDEX_EX_FMODULES) 
	{
		index |= ((fm_flags & FLAG_INDEX_EX_MASK) << INDEX_EX_SHIFT);
	}

	int temp_hx = (short)((_nFModules[off + 1] & 0xFF) + ((_nFModules[off + 2] & 0xFF) << 8));
	int temp_hy = (short)((_nFModules[off + 3] & 0xFF) + ((_nFModules[off + 4] & 0xFF) << 8));

	if ((flags & FLAG_FLIP_X) != 0) hx += temp_hx;
	else hx -= temp_hx;
	if ((flags & FLAG_FLIP_Y) != 0) hy += temp_hy;
	else hy -= temp_hy;
	
	if (USE_HYPER_FM && ((fm_flags & FLAG_HYPER_FM) != 0)) 
	{
		PaintFrame3D(index, pos, flags ^ (fm_flags & 0x0F), hx, hy, xscale, yscale);
	} 
	else 
	{
		if ((flags & FLAG_FLIP_X) != 0) hx += _modules_w[index];
		if ((flags & FLAG_FLIP_Y) != 0) hy += _modules_h[index];

		PaintModule3D(index, pos, flags ^ (fm_flags & 0x0F), hx, hy, xscale, yscale);
	}
}

void ASprite::PaintModule3D(int module, const core::vector3df& pos, int flags, f32 hx, f32 hy, f32 xscale, f32 yscale)
{
	int srcPosX = _modules_x[module];
	int srcPosY = _modules_y[module];
	int sizeX = _modules_w[module];
	int sizeY = _modules_h[module];

	if (sizeX <= 0 || sizeY <= 0)
		return;

	video::IVideoDriver* driver = g->driver;
	ICameraSceneNode* camera = g->device->getSceneManager()->getActiveCamera();

	if (_material != NULL)
	{
		if ((flags & FLAG_FLIP_X) != 0) hx += (sizeX>>1);
		else hx -= (sizeX>>1);
		if ((flags & FLAG_FLIP_Y) != 0) hy += (sizeY>>1);
		else hy -= (sizeY>>1);

		core::vector3df campos = camera->getAbsolutePosition();
		core::vector3df target = camera->getTarget();
		core::vector3df up = camera->getUpVector();
		core::vector3df view = target - campos;
		view.normalize();

		core::vector3df offset;
		offset -= _fmodules_correction*view;

		core::vector3df horizontal = up.crossProduct(view);
		if ( horizontal.getLength() == 0 )
		{
			horizontal.set(up.Y,up.X,up.Z);
		}
		horizontal.normalize();
		offset -= horizontal*hx*xscale;
		horizontal *= 0.5f*sizeX*xscale;

		core::vector3df vertical = horizontal.crossProduct(view);
		vertical.normalize();
		offset -= vertical*hy*yscale;
		vertical *= 0.5f*sizeY*yscale;

		view *= -1.0f;

		for (s32 i=0; i<4; ++i)
			vertices[i].Normal = view;

		vertices[0].Pos = pos + horizontal + vertical + offset;
		vertices[1].Pos = pos + horizontal - vertical + offset;
		vertices[2].Pos = pos - horizontal - vertical + offset;
		vertices[3].Pos = pos - horizontal + vertical + offset;

		dimension2d<s32> size = _modules_image->getSize();
		const f32 invW = 1.f / static_cast<f32>(size.Width);
		const f32 invH = 1.f / static_cast<f32>(size.Height);

		vertices[0].TCoords.set((srcPosX + sizeX)*invW, (srcPosY + sizeY)*invH);
		vertices[1].TCoords.set((srcPosX + sizeX)*invW, srcPosY*invH);
		vertices[2].TCoords.set(srcPosX*invW, srcPosY*invH);
		vertices[3].TCoords.set(srcPosX*invW, (srcPosY + sizeY)*invH);
		

		// set identity matrix
		driver->setTransform(video::ETS_WORLD, matrix4());

		//_material->ZBuffer = 0;
		driver->setMaterial(*_material);

		driver->drawIndexedTriangleList(vertices, 4, indices, 2);

		//for(int i = 0; i < 4; i++)
		//{
		//	driver->draw3DLine(vertices[i].Pos, vertices[(i + 1)%4].Pos, SColor(255, 255, 0, 0));
		//}
	}
}

int ASprite::GetFontFrameWidth(int frame)
{
    if(GetFrameColWidth(frame) == 0)
    {
        return GetFrameWidth(frame);
    }
    else
    {
        return GetFrameColWidth(frame);
    }
}

int ASprite::GetFontFrameLeft(int frame)
{
    int a = GetFrameColLeft(frame);
    return GetFrameColLeft(frame);
}

int ASprite::GetFModuleOffX(int frame, int fmodule)
{
	int off;

	int ex_off = 0;
	if ((bs_flags & BS_FM_OFF_SHORT) != 0) 
	{
		if ((ex_bs_flags & BS_FM_FREE_ROTATE_SCALE) != 0)
			off = (_frames_fm_start[frame] + fmodule) * 12;
		else
			off = (_frames_fm_start[frame] + fmodule) * 6;
	} 
	else 
	{
		off = (_frames_fm_start[frame] + fmodule) << 2;
	}

	return ((_nFModules[off + 1] & 0xFF) + ((_nFModules[off + 2] & 0xFF) << 8));
}

int ASprite::GetFModuleOffY(int frame, int fmodule)
{
	int off;

	int ex_off = 0;
	if ((bs_flags & BS_FM_OFF_SHORT) != 0) 
	{
		if ((ex_bs_flags & BS_FM_FREE_ROTATE_SCALE) != 0)
			off = (_frames_fm_start[frame] + fmodule) * 12;
		else
			off = (_frames_fm_start[frame] + fmodule) * 6;
	} 
	else 
	{
		off = (_frames_fm_start[frame] + fmodule) << 2;
	}

	return ((_nFModules[off + 3] & 0xFF) + ((_nFModules[off + 4] & 0xFF) << 8));
}
